package com.qijia.common.dao.impl;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceException;
import javax.persistence.Query;

import org.springframework.orm.jpa.JpaCallback;

import com.qijia.common.dao.JpaBaseDaoSupport;
import com.qijia.common.domain.BaseData;
import com.qijia.common.utils.ListUtils;
import com.qijia.common.utils.PageUtil;

public abstract class JpaDaoSupportImpl<T extends BaseData> extends
		org.springframework.orm.jpa.support.JpaDaoSupport implements
		JpaBaseDaoSupport<T> {

	protected Class<T> clazz;
	
	public Class<T> getClazz() {
		return clazz;
	}

	public void setClazz(Class<T> clazz) {
		this.clazz = clazz;
	}
	
	public void refresh(Object obj){
		this.getJpaTemplate().refresh(obj);
	}

	private EntityManagerFactory entityManagerFactory;

	public void setEntityManagerFty() {
		super.setEntityManagerFactory(entityManagerFactory);
	}

	public void delete(T entity) {
		logger.info("deleting " + entity.getEntityName() + " instance");
		try {
			entity = this.getJpaTemplate().getReference(clazz, entity.getPkKey());
			getJpaTemplate().remove(entity);
			logger.info("delete successful");
		} catch (RuntimeException re) {
			logger.error("delete failed", re);
			throw re;
		}
	}

	@SuppressWarnings("unchecked")
	public List<T> findAll(final int... rowStartIdxAndCount) {
		logger.info("finding all CommonResource instances");
		try {
			final String queryString = "select model from "
					+ clazz.getSimpleName() + " model";
			return getJpaTemplate().executeFind(new JpaCallback<Object>() {
				public List<T> doInJpa(EntityManager em)
						throws PersistenceException {
					Query query = em.createQuery(queryString);
					if (rowStartIdxAndCount != null
							&& rowStartIdxAndCount.length > 0) {
						int rowStartIdx = Math.max(0, rowStartIdxAndCount[0]);
						if (rowStartIdx > 0) {
							query.setFirstResult(rowStartIdx);
						}

						if (rowStartIdxAndCount.length > 1) {
							int rowCount = Math.max(0, rowStartIdxAndCount[1]);
							if (rowCount > 0) {
								query.setMaxResults(rowCount);
							}
						}
					}
					return query.getResultList();
				}
			});
		} catch (RuntimeException re) {
			logger.error("find all failed", re);
			throw re;
		}
	}

	T t;
	public T findById(Integer id) {
		logger.info("finding " + clazz.getSimpleName() + " instance with id: " + id);
		try {
			T instance = getJpaTemplate().find(clazz, id);
			return instance;
		} catch (RuntimeException re) {
			logger.error("find failed", re);
			throw re;
		}
	}

	@SuppressWarnings("unchecked")
	public List<T> findByProperty(String propertyName, final Object value,
			final int... rowStartIdxAndCount) {
		logger.info("finding " + clazz.getSimpleName()
				+ " instance with property: " + propertyName + ", value: "
				+ value);
		try {
			final String queryString = "select model from "
					+ clazz.getSimpleName() + " model where model."
					+ propertyName + "= :propertyValue";
			return getJpaTemplate().executeFind(new JpaCallback<Object>() {
				public List<T> doInJpa(EntityManager em)
						throws PersistenceException {
					Query query = em.createQuery(queryString);
					query.setParameter("propertyValue", value);
					if (rowStartIdxAndCount != null
							&& rowStartIdxAndCount.length > 0) {
						int rowStartIdx = Math.max(0, rowStartIdxAndCount[0]);
						if (rowStartIdx > 0) {
							query.setFirstResult(rowStartIdx);
						}

						if (rowStartIdxAndCount.length > 1) {
							int rowCount = Math.max(0, rowStartIdxAndCount[1]);
							if (rowCount > 0) {
								query.setMaxResults(rowCount);
							}
						}
					}
					return query.getResultList();
				}
			});
		} catch (RuntimeException re) {
			logger.error("find by property name failed", re);
			throw re;
		}
	}

	public PageUtil findForPge(PageUtil page) {
		// TODO Auto-generated method stub
		return null;
	}

	public void save(T entity) {
		logger.info("saving " + entity.getEntityName() + " instance");
		try {
			getJpaTemplate().persist(entity);
			logger.info("save successful");
		} catch (RuntimeException re) {
			logger.error("save failed", re);
			throw re;
		}
	}

	public T update(T entity) {
		logger.info("updating " + entity.getEntityName() + " instance");
		try {
			T result = getJpaTemplate().merge(entity);
			logger.info("update successful");
			return result;
		} catch (RuntimeException re) {
			logger.error("update failed", re);
			throw re;
		}
	}

	public Integer countByParam(String propertyName, final Object value) {
		logger.info("count " + clazz.getSimpleName()
				+ " instance with property: " + propertyName + ", value: "
				+ value);
		try {
			final String queryString = "count model from "
					+ clazz.getSimpleName() + " model where model."
					+ propertyName + "= :propertyValue";
			return (Integer) getJpaTemplate().execute(new JpaCallback<Object>() {
				public Object doInJpa(final EntityManager em)
						throws PersistenceException {
					final Query query = em.createQuery(queryString);
					query.setParameter("propertyValue", value);
					return query.getResultList();
				}
			});
		} catch (RuntimeException re) {
			logger.error("find by property name failed", re);
			throw re;
		}
	}

	public String buildQueryString(final List<String> propertyNames,
			final List<Object> values) {
		StringBuilder builder = new StringBuilder();
		builder.append("select model from ");
		builder.append(getClass().getSimpleName());

		if (ListUtils.isEmpty(propertyNames) || ListUtils.isEmpty(values)) {
			return builder.toString();
		} else if (propertyNames.size() != values.size()) {
			return builder.toString();
		}

		builder.append(" where ");

		for (int i = 0; i < propertyNames.size(); i++) {
			if (i > 0) {
				builder.append(" and ");
			}

			builder.append(propertyNames.get(i) + "=?");
		}
		
		return builder.toString();
	}

	@SuppressWarnings("unchecked")
	public List<T> findBySql(final String queryString,
			final List<Object> values, final int... rowStartIdxAndCount) {
		logger.info("finding " + clazz.getSimpleName() + " instance with sql: "
				+ queryString);
		try {
			return getJpaTemplate().executeFind(new JpaCallback<Object>() {
				public List<T> doInJpa(EntityManager em)
						throws PersistenceException {
					Query query = em.createQuery(queryString);
					if (!ListUtils.isEmpty(values)) {
						for (int i = 0; i < values.size(); i++) {
							query.setParameter(i, values.get(i));
						}
					}

					if (rowStartIdxAndCount != null
							&& rowStartIdxAndCount.length > 0) {
						int rowStartIdx = Math.max(0, rowStartIdxAndCount[0]);
						if (rowStartIdx > 0) {
							query.setFirstResult(rowStartIdx);
						}

						if (rowStartIdxAndCount.length > 1) {
							int rowCount = Math.max(0, rowStartIdxAndCount[1]);
							if (rowCount > 0) {
								query.setMaxResults(rowCount);
							}
						}
					}
					return query.getResultList();
				}
			});
		} catch (RuntimeException re) {
			logger.error("find by property name failed", re);
			throw re;
		}
	}
	
	@SuppressWarnings("unchecked")
	public List<T> findBySql(final String queryString,
			final Object value, final int... rowStartIdxAndCount) {
		logger.debug("finding " + clazz.getSimpleName() + " instance with sql: "
				+ queryString+",with param="+value);
	
		try {
			return getJpaTemplate().executeFind(new JpaCallback<Object>() {
				public List<T> doInJpa(EntityManager em)
						throws PersistenceException {
					Query query = em.createQuery(queryString);
					if (null!=value) {
							query.setParameter(0, value);
					}

					if (rowStartIdxAndCount != null
							&& rowStartIdxAndCount.length > 0) {
						int rowStartIdx = Math.max(0, rowStartIdxAndCount[0]);
						if (rowStartIdx > 0) {
							query.setFirstResult(rowStartIdx);
						}

						if (rowStartIdxAndCount.length > 1) {
							int rowCount = Math.max(0, rowStartIdxAndCount[1]);
							if (rowCount > 0) {
								query.setMaxResults(rowCount);
							}
						}
					}
					return query.getResultList();
				}
			});
		} catch (RuntimeException re) {
			logger.error("find by property name failed", re);
			throw re;
		}
	}

}
